//
// Created by 0209 on 2025/8/29.
//
#include "Serial.h"


/*
 * 构造函数
 * 初始化串口对象，父对象用于QT的内存管理
 */
SERIAL::SERIAL(QObject* parent) : QObject(parent)
{
    connect(&serial, &QSerialPort::readyRead,
            this, &SERIAL::onReadyRead);
}

/*
 * 析构函数
 * 如果串口仍然打开，关闭串口
 */
SERIAL::~SERIAL()
{
    if (serial.isOpen()) serial.close();
}

/*
* 槽函数: 处理底层 QSerialPort 的 readyRead 信号
* 读取串口缓冲区数据并发射 ReceiveData 信号
*/
void SERIAL::onReadyRead()
{
    QByteArray data = serial.readAll();
    emit ReceiveData(data); // 发射信号给外部
}


/*
 * 查询串口是否打开
 */
bool SERIAL::isOpen() const
{
    return serial.isOpen();
}
/*
 * 关闭串口函数
 */
void SERIAL::SetSerialClose()
{
    serial.close();
    qDebug() << "串口已关闭:" << serial.portName();
}


/*
 * 获取当前可用串口列表
 */
QStringList SERIAL::scanPorts()
{
    QStringList ports;
    auto available = QSerialPortInfo::availablePorts();

    // 使用 lambda 排序（按端口号数字大小排序，而不是字符串）
    std::sort(available.begin(), available.end(),
              [](const QSerialPortInfo& a, const QSerialPortInfo& b)
              {
                  auto extractNumber = [](const QString &portName) -> int {
                      QString digits;
                      for (QChar c : portName) {
                          if (c.isDigit())
                              digits += c;
                      }
                      // 若没有数字则返回一个较大数，避免干扰排序
                      return digits.isEmpty() ? 9999 : digits.toInt();
                  };

                  int numA = extractNumber(a.portName());
                  int numB = extractNumber(b.portName());
                  if (numA == numB)
                      return a.portName() < b.portName();  // 备用比较，防止相同号名冲突
                  return numA < numB;
              });

    for (const auto& info : available)
        ports << info.portName();

    return ports;
}


/*
 * 设置端口号
 */
void SERIAL::setPortName(const QString& portName)
{
    serial.setPortName(portName);
    qDebug() << "串口:" << serial.portName();
}

/*
 * 设置端口描述
 */
QString SERIAL::currentPortDescription() const
{
    // 获取当前串口名称
    QString name = serial.portName();

    // 遍历系统串口，找到匹配的串口信息
    for (const QSerialPortInfo& info : QSerialPortInfo::availablePorts())
    {
        if (info.portName() == name)
        {
            return info.description(); // 返回设备描述，例如 "USB-SERIAL CH340"
        }
    }
    return QString("未知设备");
}


/*
 * 设置波特率
 */
void SERIAL::SetSerialBaudRate(const QString& baudRateStr)
{
    //尝试将其转换成为整数
    bool ok = false;
    int baudRate = baudRateStr.toInt(&ok);
    //设置波特率
    serial.setBaudRate(baudRate);
    qDebug() << "串口波特率设置成功:" << baudRate;
}


/*
 * 设置字符串的流控方式
 */
void SERIAL::SetSerialFlowControl(const QString& flowControlStr)
{
    if (flowControlStr == "None")
    {
        serial.setFlowControl(QSerialPort::NoFlowControl);
        qDebug() << "流控设置: None";
    }
    else if (flowControlStr == "Hardware")
    {
        serial.setFlowControl(QSerialPort::HardwareControl);
        qDebug() << "流控设置: Hardware 硬件流控";
    }
    else if (flowControlStr == "Software")
    {
        serial.setFlowControl(QSerialPort::SoftwareControl);
        qDebug() << "流控设置: Software 软件流控";
    }
    else
    {
        qDebug() << "未知的流控参数:" << flowControlStr;
    }
}

/*
 * 设置串口校验位
 */
void SERIAL::SetSerialParity(const QString& parityStr)
{
    if (parityStr == "None")
    {
        serial.setParity(QSerialPort::NoParity);
        qDebug() << "校验位设置: None";
    }
    else if (parityStr == "Even")
    {
        serial.setParity(QSerialPort::EvenParity);
        qDebug() << "校验位设置: Even 偶校验";
    }
    else if (parityStr == "Odd")
    {
        serial.setParity(QSerialPort::OddParity);
        qDebug() << "校验位设置: Odd 奇校验";
    }
    else if (parityStr == "Mark")
    {
        serial.setParity(QSerialPort::MarkParity);
        qDebug() << "校验位设置: Mark";
    }
    else if (parityStr == "Space")
    {
        serial.setParity(QSerialPort::SpaceParity);
        qDebug() << "校验位设置: Space";
    }
    else
    {
        qDebug() << "未知的校验位参数:" << parityStr;
    }
}

/*
 * 数据位设置
 */
void SERIAL::SetSerialDataBits(const QString& dataBitsStr)
{
    if (dataBitsStr == "5")
    {
        serial.setDataBits(QSerialPort::Data5);
        qDebug() << "数据位设置: 5";
    }
    else if (dataBitsStr == "6")
    {
        serial.setDataBits(QSerialPort::Data6);
        qDebug() << "数据位设置: 6";
    }
    else if (dataBitsStr == "7")
    {
        serial.setDataBits(QSerialPort::Data7);
        qDebug() << "数据位设置: 7";
    }
    else if (dataBitsStr == "8")
    {
        serial.setDataBits(QSerialPort::Data8);
        qDebug() << "数据位设置: 8";
    }
    else
    {
        qDebug() << "未知的数据位参数:" << dataBitsStr;
    }
}

/*
 * 停止位
 */
void SERIAL::SetSerialStopBits(const QString& stopBitsStr)
{
    if (stopBitsStr == "1")
    {
        serial.setStopBits(QSerialPort::OneStop);
        qDebug() << "停止位设置: 1";
    }
    else if (stopBitsStr == "1.5")
    {
        serial.setStopBits(QSerialPort::OneAndHalfStop);
        qDebug() << "停止位设置: 1.5";
    }
    else if (stopBitsStr == "2")
    {
        serial.setStopBits(QSerialPort::TwoStop);
        qDebug() << "停止位设置: 2";
    }
    else
    {
        qDebug() << "未知的停止位参数:" << stopBitsStr;
    }
}


//辅助函数:从串口名称提取出数字部分
int SERIAL::extractPortNumber(const QString& portName)
{
    //定义一个正则表达式，匹配COM后面的数字，忽略大小写
    QRegularExpression re("COM(\\d+)", QRegularExpression::CaseInsensitiveOption);
    //正则表达式匹配传入的串口名称
    QRegularExpressionMatch match = re.match(portName);
    //如果匹配成功，表示找到了数字部分
    if (match.hasMatch())
    {
        //获取地一个捕获组（即技术部分）并转换成整数返回
        return match.captured(1).toInt();
    }
    return 0;
}

//排比参考函数
bool SERIAL::portNumberLess(const QSerialPortInfo& a, const QSerialPortInfo& b)
{
    //提取第一个串口名称的数字部分
    int numA = extractPortNumber(a.portName());
    //提取出第二串口名称数字部分
    int numB = extractPortNumber(b.portName());
    return numA < numB;
}

/*
 * 检查当前串口是否丢失
 */
bool SERIAL::isLost(const QStringList& ports) const
{
    if (!serial.isOpen()) return false;
    return !ports.contains(serial.portName());
}

/*
 * 用可读写的方式打开串口
 */
bool SERIAL::openPort()
{
    if (!serial.isOpen())
    {
        // 以可读写方式打开
        if (serial.open(QIODevice::ReadWrite))
        {
            qDebug() << "串口打开成功:" << serial.portName();
            return true; // 打开成功
        }
        else
        {
            qDebug() << "串口打开失败:" << serial.errorString();
            return false;
        }
    }
    return true; // 已经打开
}


/*
 * @brief 获取串口打开失败的错误信息
 * @return QString 返回具体错误描述，如果没有错误返回空字符串
 *
 * 功能：
 * 1. 判断串口是否已打开，如果已打开返回空字符串
 * 2. 如果未打开，调用 QSerialPort::error() 获取错误类型
 * 3. 根据不同错误返回可读信息，包括被占用、丢失或其他异常
 */
QString SERIAL::openErrorString() const {
    if (serial.isOpen()) {
        return QString(); // 已经打开，没有错误
    }

    // 根据 QSerialPort::SerialPortError 返回具体信息
    switch (serial.error()) {
    case QSerialPort::PermissionError:
        return "串口被占用或无权限访问";
    case QSerialPort::DeviceNotFoundError:
        return "串口设备不存在或已拔出";
    case QSerialPort::OpenError:
        return "无法打开串口";
    case QSerialPort::ReadError:
        return "读取串口数据失败";
    case QSerialPort::WriteError:
        return "写入串口数据失败";
    default:
        return serial.errorString(); // 其他错误，直接返回 QSerialPort 提供的描述
    }
}


/*
 * 发送函数
 */
void SERIAL::SendData(const QString& text)
{
    if (text.isEmpty()) return;
    QByteArray data = text.toUtf8(); // 转为字节数组
    serial.write(data); // 发送
}









